﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Silverlight.PreLoading
{
    public class PreLoadingQueue : Silverlight.PreLoading.IPreLoadingQueue
    {
        readonly Dictionary<IPreLoadable, string> _queue = new Dictionary<IPreLoadable, string>();
        readonly Dictionary<IPreLoadable, string> _downloading = new Dictionary<IPreLoadable, string>();
        readonly Dictionary<IPreLoadable, string> _completed = new Dictionary<IPreLoadable, string>();
        public event EventHandler<LoadedEventArgs> OnItemPreLoaded;
        public event EventHandler<PreLoadingProgressEventArgs> OnItemProgress;
        public event EventHandler ItemFailed;
        public bool AutoAdvance { get; set; }
        public int ConnectionCount { get; set; }

        public PreLoadingQueue() {
            ConnectionCount=1;
        }

        public void Add(string key, IPreLoadable preLoadable)
        {
            _queue.Add(preLoadable, key);
            preLoadable.OnPreLoaded += PreLoadingItemCompleted;
            preLoadable.OnPreLoadingProgress += PreLoadableOnPreLoadingProgress;
            preLoadable.OnPreLoadFailed += PreLoadableFailed;
        }

        void PreLoadableFailed(object sender, EventArgs e)
        {
            if (ItemFailed != null)
                ItemFailed(this, new EventArgs());
        }

        void PreLoadableOnPreLoadingProgress(object sender, PreLoadingProgressEventArgs e)
        {
            if (OnItemProgress != null)
                OnItemProgress(this, e);
        }

        public void PreLoadAll()
        {
            AutoAdvance = true;
            while (_downloading.Count < ConnectionCount && _queue.Count > 0)
                LoadNext();
        }

        public void LoadNext()
        {
            if (Count == 0) return;

            var toLoad = _queue.Keys.First();
            _downloading.Add(toLoad, _queue[toLoad]);
            _queue.Remove(toLoad);
            toLoad.PreLoad();
        }

        public int Count
        {
            get { return _queue.Count; }
        }
        public int CompletedCount { get { return _completed.Count; } }
        public int DownloadingCount { get { return _downloading.Count; } }
        public double TotalProgress
        {
            get
            {
                double unit = (1/(double) (_queue.Count + _completed.Count + _downloading.Count));
                double downloadingProgress = _downloading.Sum(d => d.Key.Progress);
                return 
                    (unit * _completed.Count) + 
                    + (unit * downloadingProgress);
            }
        }

        public IEnumerable<IPreLoadable> Queue
        {
            get { return _queue.Keys.ToArray(); }
        }
        public IEnumerable<IPreLoadable> Downloading
        {
            get { return _downloading.Keys.ToArray(); }
        }
        public IEnumerable<IPreLoadable> Completed
        {
            get { return _completed.Keys.ToArray(); }
        }

        void PreLoadingItemCompleted(object sender, PreLoadingItemCompleteEventArgs eventArgs)
        {
            IPreLoadable item = eventArgs.Item;
            string key = _downloading[item];
            _completed.Add(item, key);
            _downloading.Remove(item);

            if (OnItemPreLoaded != null)
                OnItemPreLoaded(this, new LoadedEventArgs() { Loaded = item, Key = key});

            if (AutoAdvance)
                LoadNext();
        }
    }

    public class LoadedEventArgs : EventArgs
    {
        public string Key { get; set; }
        public IPreLoadable Loaded { get; set; }
    }
}


